/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-04
 * V4.0
 */
package com.jphenix.one.boot;

import com.jphenix.kernel.objectloader.FBeanFactory;
import com.jphenix.share.lang.SString;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.ClassLoaderUtil;
import com.jphenix.share.util.SFilesUtil;
import com.jphenix.share.util.StringUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.ver.PhenixVer;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * com.jphenix.one.boot.ServiceStartup
 * 启动包含类加载器的服务
 * @author 刘虻
 * 2013-3-27 下午7:58:02
 */
@ClassInfo({"2014-06-04 20:06","启动包含类加载器的服务"})
public class ServiceStartup {

	/**
	 * 构造函数
	 * @author 刘虻
	 */
	public ServiceStartup() {
		super();
	}

	/**
	 * 刘虻
	 * 2013-3-27 下午7:58:02
	 * @param args 传入参数  1.类加载器配置文件
	 */
	public static void main(String[] args) {
		//构建类
		ServiceStartup ss = new ServiceStartup();
		try {
			ss.startup(args); //执行启动
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 执行启动
	 * 刘虻
	 * 2013-3-27 下午8:07:29
	 * @param args 传入参数 1.类加载器配置文件
	 * @throws Exception 异常
	 * @deprecated
	 */
	public void startup(String[] args) throws Exception  {
		if(args==null || args.length<1 || args[0]==null || args[0].length()<1) {
			System.out.println("Please enter the parameter for config file path");
			return;
		}
		PhenixVer.printVERShowInfo(); //显示版本
		ArrayList<String> filePathList = new ArrayList<String>(); //类库文件序列
		//获取类库跟路径
		String libBasePath = getFilePath(getBaseClassPath(this.getClass()),false);
		getFileList(filePathList,libBasePath,null,null,true,false); //执行搜索
		for(String path:filePathList) {
			System.out.println("Load lib ->"+path);
			ClassLoaderUtil.addUrl(path);
		}
		//加入类根路径
		String path = getAllFilePath("../classes",libBasePath);
		if((new File(path)).exists()) {
    		System.out.println("Load Classes ->"+path);
    		ClassLoaderUtil.addUrl(path);
		}
		System.out.println("Begin load the config file:["+args[0]+"]");
		//构建类加载器 并执行初始化
		FBeanFactory.newInstance(args[0],args[1]);
	}
	
	
    /**
     * 获取第一个文件夹
     * @author 刘虻
     * 2008-1-30下午08:31:01
     * @param path 全路径
     * @return 第一个文件夹
     */
    protected String getFristPath(String path) {
        while(path.startsWith("/")) {
            path = path.substring(1);
        }
        //文件夹分割点
        int point = path.indexOf("/");
        if (point>-1) {
            return path.substring(0,point);
        }
        return "";
    }
	
	/**
     * 获取完整路径
     * @author 刘虻
     * 2007-6-18下午04:21:49
     * @param dummyPath 相对路径
     * @param basePath 类根路径
     * @return 完整路径
     */
    private String getAllFilePath(String dummyPath,String basePath) {
        if(basePath==null) {
            basePath = "";
        }
        basePath = BaseUtil.swapString(basePath,"\\","/");
        basePath = getFilePath(basePath,true);
        if (dummyPath==null || dummyPath.length()<1) {
            return basePath;
        }
        //转换分割符
        dummyPath = BaseUtil.swapString(dummyPath,"\\","/");
        if(dummyPath.startsWith("//")) {
            dummyPath = "/"+BaseUtil.swapString(dummyPath,"//","/");
        }else {
            dummyPath = BaseUtil.swapString(dummyPath,"//","/");
        }
        //是否为根路经
        boolean isBase = false;
        if (dummyPath.startsWith("//")
                || dummyPath.startsWith("zip:")
                || dummyPath.startsWith("file:")
                || dummyPath.indexOf(":/")==1
                || dummyPath.indexOf(":/")==2
                || getFristPath(dummyPath).equals(getFristPath(basePath))) {
            isBase = true;
        }
        if (!isBase) {
            if (!dummyPath.startsWith("/")) {
                dummyPath = "/"+dummyPath;
            }
            if(basePath.endsWith("/")) {
                basePath = basePath.substring(0,basePath.length()-1);
            }
            dummyPath = basePath+dummyPath;
        }
        dummyPath = fixUpPath(dummyPath);
        if(!(new File(dummyPath)).exists()) {
            //搜索资源获取绝对路径
            URL pathUrl = null;
            if(dummyPath.startsWith("/")) {
                pathUrl = 
                    SFilesUtil.class
                        .getClassLoader()
                            .getResource(
                                    dummyPath.substring(1));
            }else {
                pathUrl = 
                    SFilesUtil.class
                        .getClassLoader()
                            .getResource(dummyPath);
            }
            if(pathUrl==null) {
                return dummyPath;
            }
            return StringUtil.getURLDecoding(pathUrl.getPath());
        }
        return dummyPath;
    }
    
    
    /**
     * 处理路径中的通配符
     * @author 刘虻
     * 2009-11-10下午03:09:56
     * @param dummyPath 待处理路径
     * @return 处理后的路径
     */
    private String fixUpPath(String dummyPath) {
        StringBuffer rePathSbf = new StringBuffer(); //构造返回路径
        if (dummyPath.startsWith("/")) {
            rePathSbf.append("/");
            dummyPath = dummyPath.substring(1);
        }else if (dummyPath.indexOf(":/")==1) {
            rePathSbf.append(dummyPath, 0, 3);
            dummyPath = dummyPath.substring(3);
        }else if (dummyPath.indexOf(":/")==2) {
            rePathSbf.append(dummyPath, 0, 4);
            dummyPath = dummyPath.substring(4);
        }
        //分割成文件夹名
        List<String> pathSubArrayList = BaseUtil.splitToList(dummyPath,"/");
        //整理后的文件夹序列
        ArrayList<String> fixArrayList = new ArrayList<String>();
        boolean isWar = false; //是否为war
        if (pathSubArrayList!=null) {
            for (String thisPath:pathSubArrayList) {
                //endsWith("!") 是因为如果部署成war格式 war文件名与展开时的根路径名不同
                if ("..".equals(thisPath)) {
                    if (fixArrayList.size()>0 ) {
                        if (SString.valueOf(fixArrayList.get(fixArrayList.size()-1)).endsWith("!")) {
                            isWar = true;
                        }else {
                            fixArrayList.remove(fixArrayList.size()-1);
                        }
                    }
                }else {
                    if (isWar) {
                        isWar = false;
                    }else {
                        fixArrayList.add(thisPath);
                    }
                }
            }
        }
        for (int i=0;i<fixArrayList.size();i++) {
            if (i>0) {
                rePathSbf.append("/");
            }
            rePathSbf.append(fixArrayList.get(i));
        }
        return rePathSbf.toString();
    }
    
	
    /**
     * 是否为压缩文件路径
     * @author 刘虻
     * 2008-7-26下午04:08:14
     * @param urlStr 测试路径
     * @return 是否为压缩文件路径
     */
    private boolean isZipUrl(String urlStr) {
        if (urlStr==null) {
            return false;
        }
        urlStr = urlStr.toLowerCase();
        if (urlStr.startsWith("zip:")) {
            return true;
        }
        return urlStr.indexOf(".jar!/") > -1 || urlStr.indexOf(".zip!/") > -1;
    }
	
	/**
     * 从文件全路径中获取文件路径
     * @author 刘虻
     * 2006-9-13下午02:54:15
     * @param filePathStr 文件全路径
     * @param inZipPath 如果路径是zip路径，即： /zip/a.jar!/com/    ture返回zip内部路径，false，返回压缩包文件路径
     * @return 文件路径
     */
    private String getFilePath(String filePathStr,boolean inZipPath) {
        //导入参数合法化
        if (filePathStr == null 
                || filePathStr.length()==0) {
            return "";
        }
        //整理路径
        filePathStr = BaseUtil.swapString(filePathStr,"\\","/");
        int lastPoint;
        if(!inZipPath && isZipUrl(filePathStr)) {
            lastPoint = filePathStr.indexOf("!");
            if(lastPoint>0) {
                filePathStr = filePathStr.substring(0,lastPoint);
            }
        }
        if(filePathStr.startsWith("file:/")) {
            filePathStr = filePathStr.substring(6);
        }else if(filePathStr.startsWith("zip:/")) {
            filePathStr = filePathStr.substring(5);
        }
        /*
         * 如果要检测的文件没有扩展名就热闹了
         * 看起来就像文件夹名，如果文件夹名中间带个点，也挺热闹
         */
        File checkFile = new File(filePathStr);
        if(checkFile.isDirectory()) {
            return filePathStr;
        }
        lastPoint = filePathStr.lastIndexOf("/"); //最后的位置
        if (lastPoint>0) {
            return filePathStr.substring(0,lastPoint+1);
        }
        return "/";
    }
	
    /**
     * 获取类的根路径
     * 
     * 如果这个FilesUtil类在Jar包中
     * 并且Servlet容器将这个Jar包复制到缓存文件夹中运行
     * 则无法根据自身定位到类的根路径
     * 
     * 所以需要指定一个在根路径中的类
     * 
     * @author 刘虻
     * 2009-7-17上午09:43:01
     * @param inCls 在根路径下的类
     * @return
     */
    private String getBaseClassPath(Class<?> inCls) {
        if (inCls==null) {
            inCls = SFilesUtil.class;
        }
        //获取当前类URL
        URL url = 
            (inCls.getProtectionDomain()).getCodeSource().getLocation();
        String baseClassPath = url.getPath();
        //获取路径
        try {
            //从Class中获取到的路径都是UTF-8编码格式的
            baseClassPath = URLDecoder.decode(baseClassPath,"UTF-8");
        }catch(Exception e) {}
        return baseClassPath;
    }
	
	
    /**
     * 搜索指定文件夹中指定的文件，将符合条件的路径放入序列中
     * 刘虻
     * 2009-12-7 下午04:11:38
     * @param filePathList 符合条件的文件路径序列
     * @param searchPath 搜索路径（全路径）
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extName 扩展名
     * @param incChild 是否包含子文件夹
     * @param returnSubPath 是否返回相对路径
     * @throws Exception 执行发生异常
     */
    private void getFileList (
            List<String> filePathList
            ,String searchPath
            ,String fileHead
            ,final String extName
            ,boolean incChild
            ,boolean returnSubPath) throws Exception {
        //导入参数合法化
        if (filePathList==null) {
            return;
        }
        if (searchPath==null) {
            searchPath = "";
        }
        if ("//".equals(searchPath)) {
            filePathList.addAll(getDriveArrayList());
            return;
        }
        //扩展名序列
        List<String> extNameList = null;
        if(extName==null || extName.length()<1) {
        	extNameList = new ArrayList<String>();
        }else {
        	extNameList = 
                BaseUtil.splitToList(extName.toLowerCase(),";");
        }
        if (isZipUrl(searchPath)) {
            //被认为是压缩包
            searchZipFilePath(filePathList,searchPath,fileHead,extNameList,returnSubPath);
        }else {
            //搜索操作系统指定路径下的文件
            doSetFilePath(filePathList,searchPath,"",fileHead,extNameList,incChild,returnSubPath);
        }
    }

    
    /**
     * 获得指定路径下，符合文件类型的所有文件路径
     * @author 刘虻
     * @param filePathList 存放搜索后的文件路径
     * @param basePath 指定根文件夹路径（末尾不加/)
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param extNameList 文件扩展名 为空或者*则搜索所有文件  为/则只搜索文件夹名
     * @param incChild 是否包含子路径
     * @param returnSubPath 是否返回相对路径
     * Exception 获得文件路径失败
     * 2006-4-1  11:26:00
     */
    private void doSetFilePath (
            List<String> filePathList
            ,String basePath
            ,String subPath
            ,String fileHead
            ,List<String> extNameList
            ,boolean incChild
            ,boolean returnSubPath) throws Exception {
    	if(fileHead==null) {
    		fileHead = "";
    	}else {
    		fileHead = fileHead.toLowerCase();
    	}
    	final String checkFileHead = fileHead; //过滤文件名头
        //校验扩展名序列
        final ArrayList<String> checkNameArrayList = new ArrayList<String>();
        if(extNameList!=null) {
            //转换为小写
            for(Object element:extNameList) {
                if(element==null) {
                    continue;
                }
                checkNameArrayList.add(((String)element).toLowerCase());
            }
        }
        subPath = BaseUtil.swapString(subPath,"\\","/");
        basePath = BaseUtil.swapString(basePath,"\\","/");
        //构建根路径对象
        File file = new File(basePath+"/"+subPath);
        //获得当前路径下，所有符合条件的文件和文件夹
        String[] filePathArrayListStrs = 
            file.list(new FilenameFilter() {
            //文件筛选类
            @Override
            public boolean accept(File dirPathFile, String lastNameStr) {
                lastNameStr = lastNameStr.toLowerCase(); //转换为小写
                //构建查询到的文件或路径
                File file = null;
                    try{
                        if (lastNameStr==null || "/".equals(lastNameStr)) {
                            file = new File(dirPathFile.getPath());
                        }else {
                            file = new File(dirPathFile.getPath()+"/"+lastNameStr);
                        }
                    }catch(Exception e) {
                        e.printStackTrace();
                    }
                if (file != null && file.isDirectory()) {
                    //判断是否为文件夹
                    return checkFileHead.length() <= 0;
                }else {
                	if(!lastNameStr.startsWith(checkFileHead)) {
                		return false;
                	}
                    //扩展名分割点
                    int point = lastNameStr.lastIndexOf(".");
                    String extName = ""; //扩展名
                    if(point>-1) {
                        extName = lastNameStr.substring(point+1);
                    }
                    //是否为指定扩展名的文件
                    return checkNameArrayList.size() < 1
                            || checkNameArrayList.contains("*")
                            || checkNameArrayList.contains(extName);
                }
            }
        });
        //判断是否存在文件或文件夹
        if (filePathArrayListStrs != null && filePathArrayListStrs.length >0) {
            //循环筛选
            String bPath = basePath; //处理过的根路径
            if (bPath.endsWith("/")) {
                bPath = bPath.substring(0,bPath.length()-1);
            }
            String findFilePath; //查询到的文件或路径
            File findFile; //查询到的文件或文件夹
            for ( int i=0; i< filePathArrayListStrs.length; i++){
            	if(subPath.length()>0) {
            		findFilePath = subPath+"/"+filePathArrayListStrs[i];
            	}else {
            		findFilePath = filePathArrayListStrs[i];
            	}
                
                findFile = getFileByName(basePath+"/"+findFilePath,null);
                if (findFile.isDirectory() && incChild) {
                    //如果是文件夹，并且搜索子文件夹，则递归调用方法
                    doSetFilePath(filePathList,basePath,findFilePath,fileHead,checkNameArrayList,incChild,returnSubPath);
                    if (checkNameArrayList.size()<1) {
                        continue;
                    }
                    if (checkNameArrayList.contains("*") || checkNameArrayList.contains("/")) {
                    	filePathList.add(findFilePath);
                    }
                }else {
                    //加入文件路径
                	if(returnSubPath) {
                		filePathList.add(findFilePath);
                	}else {
                		filePathList.add(basePath+"/"+findFilePath);
                	}
                }
            }
        }
    }
    
    
    
    /**
     * 通过URL方式获得文件对象
     * @author 刘虻
     * @param filePathStr 文件相对路径
     * @return 对应的文件对象
     * @exception IOException 获取文件时发生异常
     * 2006-4-8  14:01:57
     */
    private File getFileByName(
            String filePathStr,String basePath) throws IOException {
        //构造返回值
        File reFile = new File(getAllFilePath(filePathStr,basePath));
        if (!reFile.exists()) {
            throw new IOException("No Find The File By Path:["
                    +filePathStr+"] AllPath:["
                    +reFile.getPath()+"] BasePath:["+basePath+"]");
        }
        return reFile;
    }
    
    
    /**
     * 通过压缩文件URL获取压缩文件物理路径
     * 
     *  如：zip:d:/zipfile/one.war!/com/Test.class
     * 
     *  返回 d:/zipfile/one.war
     * 
     * @author 刘虻
     * 2008-7-26下午03:13:49
     * @param zipFileUrl 压缩文件URL
     * @return 压缩文件物理路径
     */
    private String getZipFilePathByUrlString(String zipFileUrl) {
        if (zipFileUrl==null) {
            return null;
        }
        if (zipFileUrl.startsWith("zip:")) {
            zipFileUrl = zipFileUrl.substring(4);
        }else if(zipFileUrl.startsWith("file:")) {
            zipFileUrl = zipFileUrl.substring(5);
        }
        //获取压缩文件中子路径分隔符
        int childFilePoint = zipFileUrl.lastIndexOf("!");
        if(childFilePoint>-1) {
            zipFileUrl = zipFileUrl.substring(0,childFilePoint);
        }
        return zipFileUrl;
    }
    
    
    /**
     * 通过URL路径获取压缩文件对象
     * @author 刘虻
     * 2008-7-26下午03:11:06
     * @param zipFileUrl URL路径
     * @return 压缩文件对象
     */
    private ZipFile getZipFileByUrlString(String zipFileUrl) {
        //获取压缩文件路径
        zipFileUrl = getZipFilePathByUrlString(zipFileUrl);
        if (zipFileUrl==null) {
            return null;
        }
        try {
            return new ZipFile(zipFileUrl);
        }catch(Exception e) {}
        return null;
    }
    
    
    /**
     * 搜索压缩文件中的文件元素列表
     * @author 刘虻
     * 2008-7-26下午03:25:00
     * @param filePathList 文件元素列表序列
     * @param fileHead 搜索符合文件头名的文件（不分大小写）
     * @param basePathStr 压缩文件url
     * @param extNameList 符合条件的扩展名 支持*
     * @param returnSubPath 是否返回相对路径
     */
    private void searchZipFilePath (
            List<String> filePathList
            ,String basePathStr
            ,String fileHead
            ,List<String> extNameList
            ,boolean returnSubPath) {
        if (filePathList==null) {
            return;
        }
        if(fileHead==null) {
        	fileHead = "";
        }else {
        	fileHead = fileHead.toLowerCase();
        }
        //校验扩展名序列
        final ArrayList<String> checkNameArrayList = new ArrayList<String>();
        if(extNameList!=null) {
            //转换为小写
            for(Object element:extNameList) {
                if(element==null) {
                    continue;
                }
                checkNameArrayList.add(((String)element).toLowerCase());
            }
        }
        basePathStr = BaseUtil.swapString(basePathStr,"\\","/");
        //构建压缩文件对象
        ZipFile zf = getZipFileByUrlString(basePathStr);
        if (zf==null) {
            return;
        }
        //获取压缩文件信息迭代
        Enumeration<? extends ZipEntry> zpEnum = zf.entries();
        if (zpEnum==null) {
            return;
        }
        //获取子文件夹
        String childPath = getZipFileChildPath(basePathStr);
        if (childPath.startsWith("/")) {
            childPath = childPath.substring(1);
        }
        //获取压缩包路径
        basePathStr = getZipFilePathByUrlString(basePathStr);
        ZipEntry entry = null; //文件信息元素
        while(zpEnum.hasMoreElements()) {
            //获取元素
            entry = zpEnum.nextElement();
            if (entry==null 
                    || "..\\".equals(entry.getName())
                    || entry.isDirectory()
                ) {
                continue;
            }
            //压缩文件元素文件路径 转换为小写，扩展名搜索不分大小写
            String filePath = entry.getName().toLowerCase();
            
            //过滤掉不符合文件头名
            if(fileHead.length()>0 && !filePath.startsWith(fileHead)) {
            	continue;
            }
            
            String lastNameStr = ""; //扩展名
            //扩展名分割点
            int point = filePath.lastIndexOf(".");
            if(point>-1) {
                lastNameStr = filePath.substring(point+1);
            }
            //是否符合条件
            if (filePath.startsWith(childPath)) {
                if (checkNameArrayList.size()<1
                    || checkNameArrayList.contains("*")
                    || checkNameArrayList.contains(lastNameStr)) {
                    //这里不能将子文件路径转换成小写，否则通过小写的子路径不一定能获取到
                    //对应的压缩文件元素
                	if(returnSubPath) {
                		filePathList.add(entry.getName());
                	}else {
                		filePathList.add("zip:"+basePathStr+"!/"+entry.getName());
                	}
                }
            }
        }
    }
    
    
    /**
     * 获取压缩文件内部相对路径
     * @author 刘虻
     * 2008-7-26下午05:19:07
     * @param fileUrl 压缩文件全路径
     * @return 压缩文件内部相对路径
     */
    private String getZipFileChildPath(String fileUrl) {
        if (fileUrl==null) {
            return "/";
        }
        //获取子路径分割点
        int point = fileUrl.lastIndexOf("!");
        if (point<0) {
            return "/";
        }
        return fileUrl.substring(point+1);
    }
    
    /**
     * 获取系统有效驱动器序列
     * @author 刘虻
     * 2008-8-19下午09:41:44
     * @return 系统有效驱动器序列
     */
    private ArrayList<String> getDriveArrayList() {
        //构造返回值
        ArrayList<String> reArrayList = new ArrayList<String>();
        String checkDrive = null; //检测盘符
        File checkFile = null; //检测文件
        for(int i=65;i<91;i++) {
            checkDrive = (char) i +":";
            checkFile = new File(checkDrive);
            if (checkFile.exists()) {
                reArrayList.add(checkDrive);
            }
        }
        return reArrayList;
    }
}
